/*  PCSX2 - PS2 Emulator for PCs
 *  Copyright (C) 2002-2010  PCSX2 Dev Team
 *
 *  PCSX2 is free software: you can redistribute it and/or modify it under the terms
 *  of the GNU Lesser General Public License as published by the Free Software Found-
 *  ation, either version 3 of the License, or (at your option) any later version.
 *
 *  PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE.  See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with PCSX2.
 *  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * ix86 core v0.9.1
 *
 * Original Authors (v0.6.2 and prior):
 *		linuzappz <linuzappz@pcsx.net>
 *		alexey silinov
 *		goldfinger
 *		zerofrog(@gmail.com)
 *
 * Authors of v0.9.1:
 *		Jake.Stine(@gmail.com)
 *		cottonvibes(@gmail.com)
 *		sudonim(1@gmail.com)
 */

#include "PrecompiledHeader.h"
#include "internal.h"

namespace x86Emitter
{

void xImpl_JmpCall::operator()(const xRegisterInt &absreg) const { xOpWrite(0, 0xff, isJmp ? 4 : 2, absreg); }
void xImpl_JmpCall::operator()(const xIndirect64orLess &src) const { xOpWrite(0, 0xff, isJmp ? 4 : 2, src); }

const xImpl_JmpCall xJMP = {true};
const xImpl_JmpCall xCALL = {false};

const xImpl_FastCall xFastCall = {};

void xSmartJump::SetTarget()
{
    u8 *target = xGetPtr();
    if (m_baseptr == NULL)
        return;

    xSetPtr(m_baseptr);
    u8 *const saveme = m_baseptr + GetMaxInstructionSize();
    xJccKnownTarget(m_cc, target, true);

    // Copy recompiled data inward if the jump instruction didn't fill the
    // alloted buffer (means that we optimized things to a j8!)

    const int spacer = (sptr)saveme - (sptr)xGetPtr();
    if (spacer != 0) {
        u8 *destpos = xGetPtr();
        const int copylen = (sptr)target - (sptr)saveme;

        memcpy(destpos, saveme, copylen);
        xSetPtr(target - spacer);
    }
}

xSmartJump::~xSmartJump()
{
    SetTarget();
    m_baseptr = NULL; // just in case (sometimes helps in debugging too)
}

// ------------------------------------------------------------------------
// Emits a 32 bit jump, and returns a pointer to the 32 bit displacement.
// (displacements should be assigned relative to the end of the jump instruction,
// or in other words *(retval+1) )
__emitinline s32 *xJcc32(JccComparisonType comparison, s32 displacement)
{
    if (comparison == Jcc_Unconditional)
        xWrite8(0xe9);
    else {
        xWrite8(0x0f);
        xWrite8(0x80 | comparison);
    }
    xWrite<s32>(displacement);

    return ((s32 *)xGetPtr()) - 1;
}

// ------------------------------------------------------------------------
// Emits a 32 bit jump, and returns a pointer to the 8 bit displacement.
// (displacements should be assigned relative to the end of the jump instruction,
// or in other words *(retval+1) )
__emitinline s8 *xJcc8(JccComparisonType comparison, s8 displacement)
{
    xWrite8((comparison == Jcc_Unconditional) ? 0xeb : (0x70 | comparison));
    xWrite<s8>(displacement);
    return (s8 *)xGetPtr() - 1;
}

// ------------------------------------------------------------------------
// Writes a jump at the current x86Ptr, which targets a pre-established target address.
// (usually a backwards jump)
//
// slideForward - used internally by xSmartJump to indicate that the jump target is going
// to slide forward in the event of an 8 bit displacement.
//
__emitinline void xJccKnownTarget(JccComparisonType comparison, const void *target, bool slideForward)
{
    // Calculate the potential j8 displacement first, assuming an instruction length of 2:
    sptr displacement8 = (sptr)target - (sptr)(xGetPtr() + 2);

    const int slideVal = slideForward ? ((comparison == Jcc_Unconditional) ? 3 : 4) : 0;
    displacement8 -= slideVal;

    if (slideForward) {
        pxAssertDev(displacement8 >= 0, "Used slideForward on a backward jump; nothing to slide!");
    }

    if (is_s8(displacement8))
        xJcc8(comparison, displacement8);
    else {
        // Perform a 32 bit jump instead. :(
        s32 *bah = xJcc32(comparison);
        sptr distance = (sptr)target - (sptr)xGetPtr();

#ifdef __x86_64__
        // This assert won't physically happen on x86 targets
        pxAssertDev(distance >= -0x80000000LL && distance < 0x80000000LL, "Jump target is too far away, needs an indirect register");
#endif

        *bah = (s32)distance;
    }
}

// Low-level jump instruction!  Specify a comparison type and a target in void* form, and
// a jump (either 8 or 32 bit) is generated.
__emitinline void xJcc(JccComparisonType comparison, const void *target)
{
    xJccKnownTarget(comparison, target, false);
}

xForwardJumpBase::xForwardJumpBase(uint opsize, JccComparisonType cctype)
{
    pxAssert(opsize == 1 || opsize == 4);
    pxAssertDev(cctype != Jcc_Unknown, "Invalid ForwardJump conditional type.");

    BasePtr = (s8 *)xGetPtr() +
              ((opsize == 1) ? 2 :                           // j8's are always 2 bytes.
                   ((cctype == Jcc_Unconditional) ? 5 : 6)); // j32's are either 5 or 6 bytes

    if (opsize == 1)
        xWrite8((cctype == Jcc_Unconditional) ? 0xeb : (0x70 | cctype));
    else {
        if (cctype == Jcc_Unconditional)
            xWrite8(0xe9);
        else {
            xWrite8(0x0f);
            xWrite8(0x80 | cctype);
        }
    }

    xAdvancePtr(opsize);
}

void xForwardJumpBase::_setTarget(uint opsize) const
{
    pxAssertDev(BasePtr != NULL, "");

    sptr displacement = (sptr)xGetPtr() - (sptr)BasePtr;
    if (opsize == 1) {
        pxAssertDev(is_s8(displacement), "Emitter Error: Invalid short jump displacement.");
        BasePtr[-1] = (s8)displacement;
    } else {
        // full displacement, no sanity checks needed :D
        ((s32 *)BasePtr)[-1] = displacement;
    }
}

// returns the inverted conditional type for this Jcc condition.  Ie, JNS will become JS.
__fi JccComparisonType xInvertCond(JccComparisonType src)
{
    pxAssert(src != Jcc_Unknown);
    if (Jcc_Unconditional == src)
        return Jcc_Unconditional;

    // x86 conditionals are clever!  To invert conditional types, just invert the lower bit:
    return (JccComparisonType)((int)src ^ 1);
}
}
